package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"gopkg.in/yaml.v3"
	"gorm.io/gorm"

	"github.com/google/uuid"
	"github.com/wailsapp/wails/v2/pkg/runtime"
)

// App struct
type App struct {
	ctx context.Context
}

// NewApp creates a new App application struct
func NewApp() *App {
	return &App{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
}

func (a *App) SaveDocument(note Note) (Note, error) {
	if note.ID == 0 && note.CreateTime.IsZero() {
		note.CreateTime = time.Now()
	}
	err := db.Transaction(func(tx *gorm.DB) error {
		err := tx.Omit("Tags").Save(&note).Error
		if err != nil {
			return err
		}
		err = SaveTag(tx, note)
		if err != nil {
			return err
		}
		return nil
	})

	return note, err
}

type ResponseList struct {
	Data  []Note `json:"data"`
	Total int64  `json:"total"`
}

func (a *App) List(pageNumber int, pageSize int, tagIds []int) (ResponseList, error) {
	if pageSize == 0 {
		pageSize = 10
	}

	dbInstance := db.Model(&Note{})

	if len(tagIds) > 0 {
		for i, tagID := range tagIds {
			joins := fmt.Sprintf("JOIN note_tag nt%d ON note.id = nt%d.note_id AND nt%d.tag_id = ?", i, i, i)
			dbInstance = dbInstance.Joins(joins, tagID)
		}
	}

	var count int64
	err := dbInstance.Model(&Note{}).Count(&count).Error
	if err != nil {
		return ResponseList{}, err
	}

	var notes []Note
	dbInstance = dbInstance.Offset((pageNumber - 1) * pageSize).Limit(pageSize).Preload("Tags").Order("create_time DESC")
	err = dbInstance.Find(&notes).Error

	return ResponseList{
		Data:  notes,
		Total: count,
	}, err
}

func (a *App) GetHeatMap(startTime, endTime string) ([]HeatMap, error) {
	var heatMap []HeatMap
	err := db.Table("note").Select("strftime('%Y-%m-%d', create_time) date", "count(*) count").
		Where("create_time BETWEEN ? and ?", startTime, endTime).Group("date").
		Find(&heatMap).Error

	return heatMap, err
}

func (a *App) GetNote(id int) (Note, error) {
	var note Note
	err := db.Where("id = ?", id).Preload("Tags").First(&note).Error
	fmt.Println(note.Tags)
	return note, err
}

func (a *App) DeleteNote(id int) error {
	return db.Transaction(func(tx *gorm.DB) error {
		err := tx.Delete(&NoteTag{}, "note_id = ?", id).Error
		if err != nil {
			return err
		}
		return tx.Delete(&Note{}, id).Error
	})
}

func (a *App) OpenFile() (string, error) {
	return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{})
}

func (a *App) OpenDirectory() (string, error) {
	return runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{})
}

func (a *App) UpdateConfig(config Config) error {
	b, err := yaml.Marshal(config)
	if err != nil {
		return err
	}

	err = os.WriteFile(GetConfigPath(), b, os.ModePerm)
	if err != nil {
		return err
	}
	globalConfig.DBPath = config.DBPath
	globalConfig.AttachmentPath = config.AttachmentPath

	UpdateStaticFile()
	err = InitDataSource()
	if err != nil {
		return err
	}

	return err
}

func (a *App) GetConfig() Config {
	return *globalConfig
}

func (a *App) SelectFileToSave() (*UploadInfo, error) {
	selectedFilePath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{})
	if err != nil {
		return nil, err
	}

	_, originFileName := filepath.Split(selectedFilePath)
	file, err := os.Open(selectedFilePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	ext := filepath.Ext(selectedFilePath)

	uuidName := uuid.NewString() + ext
	targetPath := filepath.Join(GetAbsPath(globalConfig.AttachmentPath), uuidName)
	targetFile, err := os.Create(targetPath)
	if err != nil {
		return nil, err
	}
	defer targetFile.Close()

	_, err = io.Copy(targetFile, file)
	if err != nil {
		return nil, err
	}

	return &UploadInfo{
		FileName:       uuidName,
		OriginFileName: originFileName,
	}, err
}

func (a *App) TagList() ([]Tag, error) {
	tags := make([]Tag, 0)
	err := db.Find(&tags).Error
	return tags, err
}

func (a *App) DeleteTag(id int) error {
	count := int64(0)
	// select count(*) from note_tag where tag_id = ?
	err := db.Model(&NoteTag{}).Where("tag_id = ?", id).Count(&count).Error
	if err != nil {
		return err
	}
	if count > 0 {
		return fmt.Errorf("标签使用中！")
	}
	return db.Delete(&Tag{}, id).Error
}

func (a *App) UpdateTag(tag Tag) error {
	return db.Save(&tag).Error
}
